#ifndef __MEMLEAK_H__
#define __MEMLEAK_H__
#include <stdio.h>
#include <stdlib.h>

static inline void raw_free(void *x)
{
    free(x);
}

#ifdef DBG_MEM

#include "log/log.h"
#include "atomic/atomic.h"
#include "int2human/int2human.h"
#include "list/list.h"
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <pthread.h>

struct mem_node {
    struct list_head node;
    atomic_t alloc_count;
    atomic_t free_count;
    atomic_t mem_count;
    const char *file;
    int line;
    const char *func;
};

struct memleak_handle {
    struct memleak_handle *me_addr;
    struct list_head mem_node_list;
    pthread_mutex_t lock;
};

extern struct memleak_handle memleak_handle;
#undef  MEMLEAK_INIT
#define MEMLEAK_INIT do {\
    if (memleak_handle.me_addr == &memleak_handle) die("memleak_handle inited, can not call me again\n"); \
    memleak_handle.me_addr = &memleak_handle; \
    INIT_LIST_HEAD(&memleak_handle.mem_node_list);\
    pthread_mutex_init(&memleak_handle.lock, NULL);\
} while(0)

#define DUMP_MEMSTAT_JUSTTOTAL 1
#define DUMP_MEMSTAT_DETAIL 2
static inline int memleak_stat(FILE *fp, char *buf, int buf_len, int type, int html_switch)
{
    int data_len = 0, ret;
    uint64_t mem_total = 0;
    struct mem_node *node;
    char tmp[512];
    list_for_each_entry(node, &memleak_handle.mem_node_list, node) {
        if (type == DUMP_MEMSTAT_DETAIL) {
            sprintf(tmp, "mem %-10u count %6u=%u-%u", atomic_read(&node->mem_count), atomic_read(&node->alloc_count)-atomic_read(&node->free_count), atomic_read(&node->alloc_count), atomic_read(&node->free_count));
            if (fp) {
                ret = fprintf(fp, "%-47s file %s func %s line %d\n", tmp, node->file, node->func, node->line);
            } else if (buf) {
                ret = snprintf(buf + data_len, buf_len - data_len, "%-47s file %s func %s line %d\n", tmp, node->file, node->func, node->line);
                data_len += ret; if (data_len >= buf_len) data_len = buf_len-1;
                buf[data_len] = 0;
            } else {
                LLOG("%-47s file %s func %s line %d\n", tmp, node->file, node->func, node->line);
            }
        }
        mem_total += atomic_read(&node->mem_count);
    }
    char mem_total_str[128] = ""; int2human_4(html_switch, 1, mem_total, mem_total_str, 0, "");
    if (type == DUMP_MEMSTAT_DETAIL || type == DUMP_MEMSTAT_JUSTTOTAL) {
        if (fp) {
            fprintf(fp, "mem_total %s\n", mem_total_str);
        } else if (buf) {
            ret = snprintf(buf + data_len, buf_len - data_len, "mem_total %s\n", mem_total_str);
            data_len += ret; if (data_len >= buf_len) data_len = buf_len-1;
            buf[data_len] = 0;
        } else {
            LLOG("mem_total %s\n", mem_total_str);
        }
    }
    return data_len;
}

#define MEMLEAK_HEAD_SIZE (8+sizeof(long))
#define check_overleap(x) do { \
        uint8_t *u8 = (uint8_t *)(x); int *m = (int *)(u8 - MEMLEAK_HEAD_SIZE); if (m[0] != 0x01020304) die("checkout MEMLEAK_HEAD creaed\n"); \
        uint8_t *end=u8 + m[1]; if (memcmp(end, "\x04\x03\x02\x01", 4)) die("checkout MEMLEAK_TAIL crashed\n"); \
} while(0)

#define free(x) ({ \
        check_overleap(x); \
        uint8_t *u8 = (uint8_t *)(x); int *m = (int *)(u8 - MEMLEAK_HEAD_SIZE); \
        struct mem_node *node; memcpy(&node, m+2, sizeof(long)); \
        atomic_inc(&node->free_count); \
        atomic_sub(&node->mem_count, m[1]); \
        free(m); })
#define malloc(x) ({ \
        static struct mem_node node = { {0,0}, {0}, {0}, {0}, __FILE__, __LINE__, __func__ }; \
        if (node.node.next == NULL) { pthread_mutex_lock(&memleak_handle.lock); if (node.node.next == NULL) list_add_tail(&node.node, &memleak_handle.mem_node_list); pthread_mutex_unlock(&memleak_handle.lock); } \
        atomic_inc(&node.alloc_count); \
        int *m = malloc(MEMLEAK_HEAD_SIZE + (x) + 4); uint8_t *end = (uint8_t *)m; end += MEMLEAK_HEAD_SIZE + (x); \
        if (m) { atomic_add(&node.mem_count, x); m[0] = 0x01020304; m[1] = x; memcpy(end, "\x04\x03\x02\x01", 4); void **p = (void **)(m+2); *p = &node; m += 2 + sizeof(long) / 4; } \
        (void *)m; })
#define calloc(_x,_y) ({ int x = _x; int y = _y; \
        static struct mem_node node = { {0,0}, {0}, {0}, {0}, __FILE__, __LINE__, __func__ }; \
        if (node.node.next == NULL) { pthread_mutex_lock(&memleak_handle.lock); if (node.node.next == NULL) list_add_tail(&node.node, &memleak_handle.mem_node_list); pthread_mutex_unlock(&memleak_handle.lock); } \
        atomic_inc(&node.alloc_count); \
        int *m = calloc(1, MEMLEAK_HEAD_SIZE + (x)*(y) + 4); uint8_t *end = (uint8_t *)m; end += MEMLEAK_HEAD_SIZE + (x)*(y); \
        if (m) { atomic_add(&node.mem_count, (x)*(y)); m[0] = 0x01020304; m[1] = (x)*(y); memcpy(end, "\x04\x03\x02\x01", 4); void **p = (void **)(m+2); *p = &node; m += 2 + sizeof(long) / 4; } \
        (void *)m; })
#define realloc(x,y) ({ \
        static struct mem_node node = { {0,0}, {0}, {0}, {0}, __FILE__, __LINE__, __func__ }; \
        if (node.node.next == NULL) { pthread_mutex_lock(&memleak_handle.lock); if (node.node.next == NULL) list_add_tail(&node.node, &memleak_handle.mem_node_list); pthread_mutex_unlock(&memleak_handle.lock); } \
        atomic_inc(&node.alloc_count); \
        int *m = NULL; \
        if (x) { uint8_t *u8 = (uint8_t *)(x); m = (int *)(u8 - MEMLEAK_HEAD_SIZE); check_overleap(x); } \
        m = realloc(m, MEMLEAK_HEAD_SIZE + (y) + 4); uint8_t *end = (uint8_t *)m; end += MEMLEAK_HEAD_SIZE + (y); \
        if (m) { atomic_add(&node.mem_count, y); m[0] = 0x01020304; m[1] = y; memcpy(end, "\x04\x03\x02\x01", 4); void **p = (void **)(m+2); *p = &node; m += 2 + sizeof(long) / 4; } \
        (void *)m; })
#define strdup(s) ({ char *m = malloc(strlen(s)+1); if (m) strcpy(m, s); m; })
#define strndup(s,n) ({ int s_l = strlen(s); char *m; if (s_l > n) { m = malloc(n+1); if (m) { memcpy(m, s, n); m[n] = 0; } } else m = strdup(s); m; })

#endif
#endif
